package client

import (
	"crypto/rand"
	"distributed_db/utils"
	"math/big"
	"sync"
)

type Clerk struct {
	// 存储与多个主机之间的连接，所有主机构成 raft 管理的集群
	// 每个 server 对应一台主机
	servers []*utils.ClientEnd
	mu      sync.Mutex
	// 主服务器索引
	leader int
	// 作为 TCP 连接的事务号使用
	id int64
	// 对 Clerk 的每次 RPC 操作进行编号
	// id + commandId 构成唯一编号，可以标记操作是否被完成
	commandId uint32
}

// IncCommandId 生成并返回自增的操作编号
func (ck *Clerk) IncCommandId() uint32 {
	ck.mu.Lock()
	defer ck.mu.Unlock()
	cd := ck.commandId
	ck.commandId++
	return cd
}

// GetLeaderId 获取主服务器索引
func (ck *Clerk) GetLeaderId() int {
	ck.mu.Lock()
	defer ck.mu.Unlock()
	return ck.leader
}

// SetLeaderId 设置主服务器索引
func (ck *Clerk) SetLeaderId(value int) {
	ck.mu.Lock()
	defer ck.mu.Unlock()
	ck.leader = value
}

// nrand 生成随机的 62 位整数
// TODO: 优化随机数生成，解决随机数重复的风险
func nrand() int64 {
	// 设置最大值
	max := big.NewInt(int64(1) << 62)
	// 生成随机整数
	bigx, _ := rand.Int(rand.Reader, max)
	x := bigx.Int64()
	return x
}

// MakeClerk 生成 Clerk 对象
func MakeClerk(servers []*utils.ClientEnd) *Clerk {
	ck := new(Clerk)
	ck.servers = servers
	// 默认无主服务器索引
	ck.SetLeaderId(-1)
	ck.id = nrand()
	ck.commandId = 1
	return ck
}

// synHandleGet 向指定服务器发送 Get 请求
// 根据 key 获取对应的 value
func (ck *Clerk) synHandleGet(server int, key string, entryId uint32) utils.GetReply {
	var args utils.GetArgs
	var reply utils.GetReply
	args.Key = key
	args.ClientId = ck.id
	// 命令序列号，即 commandId
	args.EntryId = entryId
	ck.servers[server].Call("Server.Get", &args, &reply)
	return reply
}

// fetch the current value for a key.
// returns "" if the key does not exist.
// keeps trying forever in the face of all other errors.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.Get", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
func (ck *Clerk) Get(key string) string {
	// You will have to modify this function.
	id := ck.IncCommandId()
	leader := ck.GetLeaderId()
	for {
		if leader == -1 {
			leader = int(nrand()) % len(ck.servers)
		}
		reply := ck.synHandleGet(leader, key, id)
		if reply.Err == utils.OK {
			ck.SetLeaderId(leader)
			return reply.Value
		} else if reply.Err == utils.ErrWrongLeader {
			leader = -1
		} else {
			// 是leader但出现了错误，使用日志进行输出
			utils.LogC(utils.Red, "C Get Failed %v\n", reply.Err)
		}
	}
}

// synHandlePutAppend 向指定服务器发送 PutAppend 请求
// 如果命令执行成功，则返回 OK，否则返回其他错误码（此时需要 retry）
func (ck *Clerk) synHandlePutAppend(server int, key string, value string, op string, entryId uint32) utils.PutAppendReply {
	var args utils.PutAppendArgs
	var reply utils.PutAppendReply
	args.Key = key
	args.Value = value
	args.Op = op
	args.ClientId = ck.id
	args.EntryId = entryId
	// you can send an RPC with code like this:
	// ok := ck.servers[i].Call("KVServer.PutAppend", &args, &reply)
	// Call 返回值为 false 时，reply.Err = ""（需要重试）
	// Call 返回值为 true 时，reply.Err = utils.OK（成功） / "WrongLeader"（需要重试）
	ck.servers[server].Call("Server.PutAppend", &args, &reply)
	return reply
}

// PutAppend 被 Put 函数和 Append 函数调用
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
func (ck *Clerk) PutAppend(key string, value string, op string) {
	leader := ck.GetLeaderId()
	// 更新操作号
	id := ck.IncCommandId()
	for {
		if leader == -1 {
			leader = int(nrand()) % len(ck.servers)
		}
		reply := ck.synHandlePutAppend(leader, key, value, op, id)
		// 如果命令执行失败，则说明当前 Leader 存在问题，需要寻找其他 Leader 重试
		if reply.Err == "" || reply.Err == utils.ErrWrongLeader {
			leader = -1
		} else {
			//  找到执行成功 Leader，则将其暂存，并跳出循环
			ck.SetLeaderId(leader)
			return
		}
	}
}

func (ck *Clerk) Put(key string, value string) {
	ck.PutAppend(key, value, "Put")
}
func (ck *Clerk) Append(key string, value string) {
	ck.PutAppend(key, value, "Append")
}

// 下面是4.21新增的方法，用于与master的leader通信

// synUpdateCache 向 master 的 leader 发送更新缓存请求
// master 的 leader 将会返回最新的 Region Server 列表
func (ck *Clerk) synUpdateCache(leader int, tables []string, entryId uint32) utils.UpdateCacheReply {
	var args utils.UpdateCacheArgs
	var reply utils.UpdateCacheReply
	args.TableNames = tables
	args.ClientId = ck.id
	args.EntryId = entryId
	ck.servers[leader].Call("Server.UpdateCache", &args, &reply)
	return reply
}

func (ck *Clerk) UpdateCache(tables []string) utils.UpdateCacheReply {
	leader := ck.GetLeaderId()
	id := ck.IncCommandId()
	utils.LogC(utils.Green, "No.%v,C UpdateCache %v\n", id, tables)
	l := 1
	for {
		if leader == -1 {
			l = (l + 1) % len(ck.servers)
			leader = l
		}
		reply := ck.synUpdateCache(leader, tables, id)
		if reply.Err == utils.OK {
			ck.SetLeaderId(leader)
			utils.LogC(utils.Green, "C UpdateCache Reply %v\n", reply)
			return reply
		} else if reply.Err == utils.ErrWrongLeader {
			leader = -1
		} else { // 是leader但出现了错误
			utils.LogC(utils.Red, "C UpdateCache Failed %v\n", reply.Err)
			return reply
		}
	}
}

// synCreateTable 向 master 的 leader 发送创建表单请求
func (ck *Clerk) synCreateTable(leader int, name string, cols map[string]string, entryId uint32) utils.CreateTableReply {
	var args utils.CreateTableArgs
	var reply utils.CreateTableReply
	args.TableName = name
	args.Cols = cols
	args.ClientId = ck.id
	args.EntryId = entryId
	ck.servers[leader].Call("Server.CreateTable", &args, &reply)
	return reply
}

func (ck *Clerk) CreateTable(name string, cols map[string]string) utils.CreateTableReply {
	leader := ck.GetLeaderId()
	id := ck.IncCommandId()
	utils.LogC(utils.Green, "No.%v,C CreateTable %v\n", id, name)
	l := 1
	for {
		if leader == -1 {
			l = (l + 1) % len(ck.servers)
			leader = l
		}
		reply := ck.synCreateTable(leader, name, cols, id)
		if reply.Err == utils.OK {
			ck.SetLeaderId(leader)
			utils.LogC(utils.Green, "C CreateTable Reply %v\n", reply)
			return reply
		} else if reply.Err == utils.ErrWrongLeader {
			leader = -1
		} else { // 是leader但出现了错误
			utils.LogC(utils.Red, "C CreateTable Failed %v\n", reply.Err)
			return reply
		}
	}
}

// synDropTable 向 master 的 leader 发送删除表单请求
func (ck *Clerk) synDropTable(leader int, name string, entryId uint32) utils.DropTableReply {
	var args utils.DropTableArgs
	var reply utils.DropTableReply
	args.TableName = name
	args.ClientId = ck.id
	args.EntryId = entryId
	ck.servers[leader].Call("Server.DropTable", &args, &reply)
	return reply
}

func (ck *Clerk) DropTable(name string) utils.DropTableReply {
	leader := ck.GetLeaderId()
	id := ck.IncCommandId()
	utils.LogC(utils.Green, "No.%v,C DropTable %v\n", name, id)
	l := 1
	for {
		if leader == -1 {
			l = (l + 1) % len(ck.servers)
			leader = l
		}
		reply := ck.synDropTable(leader, name, id)
		if reply.Err == utils.OK {
			ck.SetLeaderId(leader)
			utils.LogC(utils.Green, "C DropTable Reply %v\n", reply)
			return reply
		} else if reply.Err == utils.ErrWrongLeader {
			leader = -1
		} else { // 是leader但出现了错误
			utils.LogC(utils.Red, "C DropTable Failed %v\n", reply.Err)
			return reply
		}
	}
}

// synInsertData 向 master 的 leader 发送插入数据请求
func (ck *Clerk) synInsertData(leader int, tableName string, data map[string]string, entryId uint32) utils.InsertDataReply {
	var args utils.InsertDataArgs
	var reply utils.InsertDataReply
	args.TableName = tableName
	args.Data = data
	args.ClientId = ck.id
	args.EntryId = entryId
	ck.servers[leader].Call("Server.InsertData", &args, &reply)
	return reply
}

func (ck *Clerk) InsertData(tableName string, data map[string]string) utils.InsertDataReply {
	leader := ck.GetLeaderId()
	id := ck.IncCommandId()
	utils.LogC(utils.Green, "No.%v,C InsertData %v\n", id, data)
	l := 1
	for {
		if leader == -1 {
			l = (l + 1) % len(ck.servers)
			leader = l
		}
		reply := ck.synInsertData(leader, tableName, data, id)
		if reply.Err == utils.OK {
			ck.SetLeaderId(leader)
			utils.LogC(utils.Green, "C InsertData Reply %v\n", reply)
			return reply
		} else if reply.Err == utils.ErrWrongLeader {
			utils.LogC(utils.Green, "C InsertData WrongLeader %d\n", leader)
			leader = -1
		} else { // 是leader但出现了错误
			utils.LogC(utils.Red, "C InsertData Failed %v\n", reply.Err)
			return reply
		}
	}
}

// synDeleteData 向 master 的 leader 发送删除数据请求
func (ck *Clerk) synDeleteData(leader int, tableName string, key string, entryId uint32) utils.DeleteDataReply {
	var args utils.DeleteDataArgs
	var reply utils.DeleteDataReply
	args.TableName = tableName
	args.Key = key
	args.ClientId = ck.id
	args.EntryId = entryId
	ck.servers[leader].Call("Server.DeleteData", &args, &reply)
	return reply
}

func (ck *Clerk) DeleteData(tableName string, key string) utils.DeleteDataReply {
	leader := ck.GetLeaderId()
	id := ck.IncCommandId()
	utils.LogC(utils.Green, "No.%v, C DeleteData %v\n", id, key)
	l := 1
	for {
		if leader == -1 {
			l = (l + 1) % len(ck.servers)
			leader = l
		}
		reply := ck.synDeleteData(leader, tableName, key, id)
		if reply.Err == utils.OK {
			ck.SetLeaderId(leader)
			utils.LogC(utils.Green, "C DeleteData Reply %v\n", reply)
			return reply
		} else if reply.Err == utils.ErrWrongLeader {
			leader = -1
		} else { // 是leader但出现了错误
			utils.LogC(utils.Red, "C DeleteData Failed %v\n", reply.Err)
			return reply
		}
	}
}

// Close all servers
func (ck *Clerk) Close() {
	for _, server := range ck.servers {
		server.Close()
	}
}
